package com.iflytek.jzcpx.procuracy.card.component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iflytek.jzcpx.procuracy.card.common.CardConstants;
import com.iflytek.jzcpx.procuracy.card.common.enums.XYRFieldNameEnum;
import com.iflytek.jzcpx.procuracy.card.common.enums.XyrCbsjFieldEnum;
import com.iflytek.jzcpx.procuracy.card.config.ZdbmConfig;
import com.iflytek.jzcpx.procuracy.card.entity.CardFormField;
import com.iflytek.jzcpx.procuracy.card.pojo.ElleResultData;
import com.iflytek.jzcpx.procuracy.card.pojo.ElleResultData.CompulsoryMeasureCondition;
import com.iflytek.jzcpx.procuracy.common.util.IdWokerUtil;
import com.iflytek.jzcpx.procuracy.tools.elle.ElleTextTypeEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 表单填充器抽象父类
 *
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019-08-29 19:38
 */
public abstract class AbstractFillFormHelper {
    private static final Logger logger = LoggerFactory.getLogger(AbstractFillFormHelper.class);

    @Autowired
    protected ZdbmConfig zdbmConfig;

    protected String parseXyrTableName(String ajllbm) {
        String tableName = null;
        if (StringUtils.isNotBlank(ajllbm)) {
            Map<String, String> suspects = CardConstants.XYR_TABLENAMES;
            tableName = suspects.get(ajllbm);
        }

        return tableName;
    }

    protected List<String> listAllSuppectTableNames() {
        return CardConstants.XYR_TABLENAMES.values().stream().collect(Collectors.toList());
    }

    protected void fillBgsj(JSONObject zdsj) {
        zdsj.put("bgsj", String.valueOf(System.currentTimeMillis()));
    }

    /**
     * 从原始表单中解析出需要的从表表单(单一数据)
     *
     * @param bdjg      原始表单结构
     * @param tableName 目标表单名称
     *
     * @return
     */
    protected JSONObject parseCbsjOfSingleItem(JSONObject bdjg, String tableName) {
        JSONObject cbsjJson = null;
        for (Object cbsjObj : bdjg.getJSONArray("cbsj")) {
            if (((JSONObject) cbsjObj).getString("stbm").equals(tableName)) {
                cbsjJson = (JSONObject) cbsjObj;
                break;
            }
        }
        return cbsjJson;
    }

    /**
     * 设置数据标识 01新增 11修改 12删除
     *
     * @param zdsj 字段数据
     */
    protected void setSjbs(JSONObject zdsj) {
        if (zdsj == null) {
            return;
        }

        String sjbs = zdsj.getString("sjbs");
        // 0表示赛威讯库里没有数据，此时需要将数据标识设为01
        if (sjbs.equals("0")) {
            zdsj.put("sjbs", "01");
        }
        else if (sjbs.equals("1")) {
            // 1表示赛威讯的库里已经有数据了，此时需要将数据标识设为11
            zdsj.put("sjbs", "11");
        }
    }

    /**
     * 判断字段是否需要回填
     *
     * @param zdsjJson 字段数据
     *
     * @return true: 如果变更前/后的字段文本/编码都为空
     */
    protected boolean isNeedFill(JSONObject zdsjJson) {
        if (zdsjJson == null) {
            return false;
        }
        String bgqzdwbz = zdsjJson.getString("bgqzdwbz");
        String bghzdwbz = zdsjJson.getString("bghzdwbz");
        String bgqzdbmz = zdsjJson.getString("bgqzdbmz");
        String bghzdbmz = zdsjJson.getString("bghzdbmz");

        return StringUtils.isAllBlank(bgqzdbmz, bgqzdwbz, bghzdbmz, bghzdwbz);
    }

    /**
     * 根据人员标识获取所属的案卡字段
     *
     * @param personIdentifier 人员标识
     * @param personFieldMap   人员标识与其字段
     *
     * @return 可能为 null
     */
    protected List<CardFormField> getFieldsOfPerson(PersonIdentifier personIdentifier,
            Map<PersonIdentifier, List<CardFormField>> personFieldMap) {
    	List<CardFormField> list=new ArrayList<CardFormField>();
        for (Map.Entry<PersonIdentifier, List<CardFormField>> entry : personFieldMap.entrySet()) {
            if (entry.getKey().isSame(personIdentifier)) {
            	list.addAll(entry.getValue());
            }
        }
        return list;
    }

    /**
     * 根据人员标识获取表单结构
     *
     * @param personIdentifier 人员标识
     * @param personBdjgMap    人员标识与其表单结构
     *
     * @return
     */
    protected JSONObject getBdjgByPerson(PersonIdentifier personIdentifier,
            Map<PersonIdentifier, JSONObject> personBdjgMap) {
        for (Map.Entry<PersonIdentifier, JSONObject> entry : personBdjgMap.entrySet()) {
            if (entry.getKey().isSame(personIdentifier)) {
                return entry.getValue();
            }
        }
        return null;
    }

    /** 将表单结构按 表名(stbm)进行分组 */
    protected Map<String, JSONObject> groupByStbm(JSONObject bdjgJSON) {
        Map<String, JSONObject> stbmMap = new HashMap<>();
        // 案件表在最外层
        stbmMap.put(bdjgJSON.getString("stbm"), bdjgJSON);
        // 其他表作为案件表的 cbsj(从表数据)存在
        for (Object cbsjObj : bdjgJSON.getJSONArray("cbsj")) {
            stbmMap.put(((JSONObject) cbsjObj).getString("stbm"), (JSONObject) cbsjObj);
        }
        return stbmMap;
    }

    /** 将案件表单结构按 表名(stbm)和字段名称(zdmc)进行分组 */
    protected Map<String, Map<String, JSONObject>> groupZdsjByStbmAndZdmc(JSONObject bdjgJSON) {
        Map<String, Map<String, JSONObject>> stbmMap = new HashMap<>();
        // 案件表在最外层
        stbmMap.put(bdjgJSON.getString("stbm"), mapZdsjByZdmc(bdjgJSON));
        // 其他表作为案件表的 cbsj(从表数据)存在
        for (Object cbsjObj : bdjgJSON.getJSONArray("cbsj")) {
            stbmMap.put(((JSONObject) cbsjObj).getString("stbm"), mapZdsjByZdmc((JSONObject) cbsjObj));
        }
        return stbmMap;
    }

    protected Map<String, JSONObject> mapZdsjByZdmc(JSONObject bdjgJSON) {
        return bdjgJSON.getJSONArray("zdsj").stream()
                       .collect(Collectors.toMap(e -> ((JSONObject) e).getString("zdmc"), e -> (JSONObject) e));
    }

    protected <T> T getMapValue(Map<String, Map<String, T>> map, String firstKey, String secondKey) {
        if (MapUtils.isEmpty(map) || StringUtils.isAnyBlank(firstKey, secondKey)) {
            return null;
        }

        Map<String, T> firstMap = map.get(firstKey);
        return MapUtils.isNotEmpty(firstMap) ? firstMap.get(secondKey) : null;
    }

    /**
     * 抽取嫌疑人及从表数据字段
     *
     * @param cardFileId         案卡文件 id
     * @param ajlbbm             案件类别编码
     * @param elleTextTypeEnum
     * @param extractInfoVecList elle 抽取出的数据
     *
     * @return 嫌疑人及从表数据字段
     */
    protected Collection<CardFormField> extractXyrAndCbsjFields(Long cardFileId, String ajlbbm,
            ElleTextTypeEnum elleTextTypeEnum, List<ElleResultData.ExtractInfoVec> extractInfoVecList,ElleResultData elleResultData) {
        List<CardFormField> fields = new ArrayList<>();

        // 获取嫌疑人表名
        String suspectTableName = parseXyrTableName(ajlbbm);
        if (StringUtils.isBlank(suspectTableName)) {
            logger.warn("无法根据案件类别编码：{} 获取对应的嫌疑人表名", ajlbbm);
            return new ArrayList<>();
        }

        List<ElleResultData.Suspect> suspects = extractInfoVecList.get(0).getExtractInfo().getSuspect();
        if (CollectionUtils.isNotEmpty(suspects)) {
            // 多个嫌疑人处理逻辑: 基于同一个对象进行克隆, 然后填充数据, 确保部分属性完全一致
            for (int i = 0; i < suspects.size(); i++) {
                CardFormField baseXyrField = buildXyrBaseField(cardFileId, suspectTableName);
                ElleResultData.Label label = suspects.get(i).getLabel();

                // 基于枚举配置，读取引擎抽取出的嫌疑人字段
                for (XYRFieldNameEnum xyrFieldNameEnum : XYRFieldNameEnum.values()) {
                	Collection<ElleTextTypeEnum> supportElleTextTypeEnums = xyrFieldNameEnum.getSupportElleTextType();
                    // 该字段无法从该文书中被抽取
                    if (CollectionUtils.isNotEmpty(supportElleTextTypeEnums)&&!supportElleTextTypeEnums.contains(elleTextTypeEnum)) {
                        continue;
                    }
                    fields.add(buildFieldOfXyr(SerializationUtils.clone(baseXyrField), label, xyrFieldNameEnum,elleResultData));
                }

                // 抽取出嫌疑人从表字段
                /*for (XyrCbsjFieldEnum xyrCbsjFieldEnum : XyrCbsjFieldEnum.values()) {
                    fields.add(buildFieldOfXyrCbsj(baseXyrField, label, xyrCbsjFieldEnum, elleTextTypeEnum));
                }*/
                //抽出每个嫌疑人的强制措施
                List<CardFormField> xyrCbsfields=this.buildFieldOfXyrCbsj(baseXyrField, label, elleTextTypeEnum,elleResultData);
                if(CollectionUtils.isNotEmpty(xyrCbsfields)) {
                	fields.addAll(xyrCbsfields);
                }
            }
        }

        List<CardFormField> newFields = fields.stream().filter(Objects::nonNull).collect(Collectors.toList());
        logger.info("嫌疑人字段抽取完成, 共抽取[ {} ]条字段数据. ", CollectionUtils.size(newFields));
        return newFields;
    }

    /*private CardFormField buildFieldOfXyrCbsj(CardFormField baseXyrField, ElleResultData.Label label,
            XyrCbsjFieldEnum xyrCbsjFieldEnum, ElleTextTypeEnum elleTextTypeEnum) {
        Collection<ElleTextTypeEnum> supportElleTextTypes = xyrCbsjFieldEnum.getSupportElleTextType();
        Function<ElleResultData.Label, ElleResultData.Item[]> elleFieldExtractor = xyrCbsjFieldEnum
                .getElleFieldExtractor();

        if (!supportElleTextTypes.contains(elleTextTypeEnum)) {
            // 如果该字段不在此文书类型则跳过
            return null;
        }

        ElleResultData.Item[] items = elleFieldExtractor.apply(label);
        if (ArrayUtils.isEmpty(items)) {
            // elle引擎未抽取到该字段则跳过
            return null;
        }

        // 获取引擎返回结果中的可用数据
        ElleResultData.Item usableItem = null;
        for (ElleResultData.Item item : items) {
            if (item != null && StringUtils.isNotBlank(item.getMean())) {
                usableItem = item;
                break;
            }
        }
        if (usableItem == null) {
            return null;
        }

        String zdwbz = xyrCbsjFieldEnum.convertZdwbz(usableItem.getMean());
        if (StringUtils.isBlank(zdwbz)) {
            return null;
        }

        CardFormField field = new CardFormField();
        field.setStbm(xyrCbsjFieldEnum.getStbm());
        field.setZdmc(xyrCbsjFieldEnum.getZdmc());
        field.setCardFileId(baseXyrField.getCardFileId());
        field.setCreateTime(baseXyrField.getCreateTime());
        field.setUpdateTime(baseXyrField.getUpdateTime());
        field.setPrimaryKey(baseXyrField.getPrimaryKey());
        field.setZdwbz(zdwbz);
        field.setZdbmz(xyrCbsjFieldEnum.convertZdbmzForStore(zdwbz, zdbmConfig));
        field.setRemark(new CardFormFieldRemark(usableItem).toRemarkString());

        return field;
    }*/
    
    
    private List<CardFormField> buildFieldOfXyrCbsj(CardFormField baseXyrField, ElleResultData.Label label,ElleTextTypeEnum elleTextTypeEnum,ElleResultData elleResultData) {
        
        List<CompulsoryMeasureCondition> listCompulsoryMeasure = label.getCompulsoryMeasureCondition();
		if (CollectionUtils.isEmpty(listCompulsoryMeasure)) {
			return null;
		}
		List<CardFormField> fields=new ArrayList<>();
        for (CompulsoryMeasureCondition compulsoryMeasure : listCompulsoryMeasure) {
			ElleResultData.Item qzcsItem = compulsoryMeasure.getCompulsoryMeasure();
			//从表数据主键
			String primaryCbKey=String.valueOf(IdWokerUtil.nextId());
			if (null == qzcsItem || StringUtils.isBlank(qzcsItem.getMean())) {
				continue;
			}
			for (XyrCbsjFieldEnum cbsjFieldEnum : XyrCbsjFieldEnum.values()) {
				// 字典值满足某些值才可以返回 比如：强制措施地点
				// 1.当强制措施是拘传、刑事拘留、逮捕这三种情况时，对应的是填强制措施羁押地点；
				// 2.当强制措施是取保候审时，对应的是填取保候审地点；
				// 3.当强制措施是监视居住和指定居所监视居住时，对应的是填监视居住地点
				List<String> dictionList = cbsjFieldEnum.getDictionList();

				if (CollectionUtils.isNotEmpty(dictionList) && !dictionList.contains(qzcsItem.getMean())) {
					continue;
				}
				Collection<ElleTextTypeEnum> supportElleTextType = cbsjFieldEnum.getSupportElleTextType();
				// 该字段不属于此种文书类型
				if (CollectionUtils.isNotEmpty(supportElleTextType) && !supportElleTextType.contains(elleTextTypeEnum)) {
					continue;
				}
				Function<CompulsoryMeasureCondition, ElleResultData.Item> itemFun = cbsjFieldEnum.getElleFieldExtractor();
				Function<CompulsoryMeasureCondition, ElleResultData.Item[]> itemsFun = cbsjFieldEnum.getElleFieldItemsExtractor();
				BiFunction<CompulsoryMeasureCondition,ElleResultData, ElleResultData.Item> caseFun = cbsjFieldEnum.getCaseElleFieldExtractor();
				ElleResultData.Item[] itemArray = null;
				if(null != caseFun) {
					ElleResultData.Item caseItem = caseFun.apply(compulsoryMeasure, elleResultData);
					itemArray = new ElleResultData.Item[1];
					itemArray[0] = caseItem;
				}
				else if (null != itemFun) {
					ElleResultData.Item item = itemFun.apply(compulsoryMeasure);
					if (item != null && StringUtils.isNotBlank(item.getMean())) {
						itemArray = new ElleResultData.Item[1];
						itemArray[0] = item;
					}
				}
				else if (null != itemsFun) {
					ElleResultData.Item[] items = itemsFun.apply(compulsoryMeasure);
					if (ArrayUtils.isNotEmpty(items)) {
						itemArray = items;
					}
				}
				if (ArrayUtils.isEmpty(itemArray)) {
					continue;
				}
				for (ElleResultData.Item item : itemArray) {
					if(null == item) {
						continue;
					}
					
					String zdwbz = cbsjFieldEnum.convertZdwbz(item.getMean());
			        if (StringUtils.isBlank(zdwbz)) {
			            continue;
			        }

			        CardFormField field = new CardFormField();
			        field.setStbm(cbsjFieldEnum.getStbm());
			        field.setZdmc(cbsjFieldEnum.getZdmc());
			        field.setCardFileId(baseXyrField.getCardFileId());
			        field.setCreateTime(baseXyrField.getCreateTime());
			        field.setUpdateTime(baseXyrField.getUpdateTime());
			        field.setPrimaryKey(baseXyrField.getPrimaryKey());
			        field.setZdwbz(zdwbz);
			        field.setZdbmz(cbsjFieldEnum.convertZdbmzForStore(zdwbz, zdbmConfig));
			        field.setRemark(new CardFormFieldRemark(item).toRemarkString());
			        field.setPrimaryCbkey(primaryCbKey);
			        fields.add(field);
				}
			}
		}
        return fields;
    }

    private CardFormField buildFieldOfXyr(CardFormField field, ElleResultData.Label label, XYRFieldNameEnum fieldNameEnum) {
        // 使用字段枚举中配置的函数抽取出对应的引擎字段
        ElleResultData.Item[] items = fieldNameEnum.getElleFieldExtractor().apply(label);
        if (ArrayUtils.isEmpty(items)) {
            return null;
        }

        ElleResultData.Item usableItem = null;
        for (ElleResultData.Item item : items) {
            if (item != null && StringUtils.isNotBlank(item.getMean())) {
                usableItem = item;
                break;
            }
        }
        if (usableItem == null) {
            return null;
        }

        // 对字段文本值进行格式化，例如：抽取到出生日期1990年7月10日，则格式化为1990-07-10,
        // 如果字段文本值不符合规范(如: 19x4年7月10日)则返回空, 此时不再回填
        String zdwbz = fieldNameEnum.convertZdwbz(usableItem.getMean());
        if (StringUtils.isBlank(zdwbz)) {
            return null;
        }
        field.setZdwbz(zdwbz);

        String zdbmz = fieldNameEnum.convertZdbmzForStore(zdwbz, zdbmConfig);
        field.setZdbmz(zdbmz);

        field.setZdmc(fieldNameEnum.name());

        // 记录字段位置, 便于差异页面做高亮效果
        field.setRemark(new CardFormFieldRemark(usableItem).toRemarkString());
        return field;
    }
    
	private CardFormField buildFieldOfXyr(CardFormField field, ElleResultData.Label label, XYRFieldNameEnum fieldNameEnum, ElleResultData elleResultData) {
		// 使用字段枚举中配置的函数抽取出对应的引擎字段
		Function<ElleResultData.Label, ElleResultData.Item[]> func = fieldNameEnum.getElleFieldExtractor();
		ElleResultData.Item[] items = null;
		ElleResultData.Item usableItem = null;
		if (null == func) {
			// 使用映射函数取值
			boolean hasMapFunc = fieldNameEnum.isHasMapFunc();
			if (hasMapFunc) {
				// mapFunc(qtxxArray, fieldNameEnum, label);
				Function<ElleResultData.Label, String> mapfunc = fieldNameEnum.getMappingFunc();
				BiFunction<ElleResultData.Label, ElleResultData, String> biFunc = fieldNameEnum.getCaseElleFieldExtractor();
				String zdwbz = StringUtils.EMPTY;
				if (null != mapfunc) {
					zdwbz = mapfunc.apply(label);
				}
				if (null != biFunc) {
					zdwbz = biFunc.apply(label, elleResultData);
				}
				if (StringUtils.isNotEmpty(zdwbz)) {
					field.setZdwbz(zdwbz);
					String zdbmz = fieldNameEnum.convertZdbmzForStore(zdwbz, zdbmConfig);
					field.setZdbmz(zdbmz);
					field.setZdmc(fieldNameEnum.name());
					// 记录字段位置, 便于差异页面做高亮效果
					// field.setRemark(new CardFormFieldRemark(usableItem).toRemarkString());
					return field;
				}
				return null;
			} else {
				Function<ElleResultData, ElleResultData.Item> caseFun = fieldNameEnum.getCaseMappingFunc();
				if (null != caseFun) {
					ElleResultData.Item caseItem = caseFun.apply(elleResultData);
					items = new ElleResultData.Item[1];
					items[0] = caseItem;
				}
			}
		}

		if (ArrayUtils.isEmpty(items)) {
			items = func.apply(label);
		}

		if (ArrayUtils.isEmpty(items)) {
			// 映射字段items就是为空 则取他的默认值
			boolean hasMapFunc = fieldNameEnum.isHasMapFunc();
			if (hasMapFunc) {
				// mapFunc(qtxxArray, fieldNameEnum);
				String zdwbz = fieldNameEnum.getDefaultValue();
				if (StringUtils.isNotEmpty(zdwbz)) {
					field.setZdwbz(zdwbz);
					String zdbmz = fieldNameEnum.convertZdbmzForStore(zdwbz, zdbmConfig);
					field.setZdbmz(zdbmz);
					field.setZdmc(fieldNameEnum.name());
					// 记录字段位置, 便于差异页面做高亮效果
					// field.setRemark(new CardFormFieldRemark(usableItem).toRemarkString());
					return field;
				}
				return null;
			}
			// 空值时 取其他字段作为此字段映射 比如 单位名称【与姓名共享字段】 单位性质【与身份共享字段】
			// 单位所在地址【与住所地共享字段】
			func = fieldNameEnum.getIsNullElleFieldExtractor();
			if (null != func) {
				items = func.apply(label);
				if (ArrayUtils.isEmpty(items)) {
					return null;
				}
			} else {
				return null;
			}
		}
		for (ElleResultData.Item item : items) {
			if (item != null && StringUtils.isNotBlank(item.getMean())) {
				usableItem = item;
				break;
			}
		}
		if (usableItem == null) {
			return null;
		}

		// 对字段文本值进行格式化，例如：抽取到出生日期1990年7月10日，则格式化为1990-07-10,
		// 如果字段文本值不符合规范(如: 19x4年7月10日)则返回空, 此时不再回填
		String zdwbz = fieldNameEnum.convertZdwbz(usableItem.getMean());
		if (StringUtils.isBlank(zdwbz)) {
			return null;
		}
		field.setZdwbz(zdwbz);

		String zdbmz = fieldNameEnum.convertZdbmzForStore(zdwbz, zdbmConfig);
		field.setZdbmz(zdbmz);

		field.setZdmc(fieldNameEnum.name());

		// 记录字段位置, 便于差异页面做高亮效果
		field.setRemark(new CardFormFieldRemark(usableItem).toRemarkString());
		return field;
	}

    /** 构造基础的数据项对象 */
    protected CardFormField buildXyrBaseField(Long cardFileId, String stbm) {
        CardFormField field = new CardFormField();
        field.setStbm(stbm);
        field.setCardFileId(cardFileId);
        Date now = new Date();
        field.setCreateTime(now);
        field.setUpdateTime(now);
        // 同一个条数据下多个数据项的 primaryKey 保持一致, 以便标识出多条数据项的归属
        field.setPrimaryKey(String.valueOf(IdWokerUtil.nextId()));
        return field;
    }

    protected JSONObject getCbsj(JSONObject bdjgJSON, String stbmOfCbsj) {
        if (bdjgJSON == null || StringUtils.isBlank(stbmOfCbsj)) {
            return null;
        }
        JSONArray cbsjArray = bdjgJSON.getJSONArray("cbsj");
        if (cbsjArray == null || cbsjArray.isEmpty()) {
            return null;
        }

        for (Object csbjObj : cbsjArray) {
            if (((JSONObject) csbjObj).getString("stbm").equals(stbmOfCbsj)) {
                return (JSONObject) csbjObj;
            }
        }

        return null;
    }

    /** 将从表的字段数据按从表名称-字段名称进行分组 */
    protected Map<String, Map<String, JSONObject>> mapZdsjOfCbsjByName(JSONObject bdjgJSON) {
        Map<String, Map<String, JSONObject>> stbmMap = new HashMap<>();
        for (Object cbsjObj : bdjgJSON.getJSONArray("cbsj")) {
            stbmMap.put(((JSONObject) cbsjObj).getString("stbm"), mapZdsjByZdmc((JSONObject) cbsjObj));
        }
        return stbmMap;
    }
}
